package com.zhihu.matisse.library.loader;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AsyncTaskLoader<Param,Result> {

    private final Executor sDefaultExecutor = Executors.newCachedThreadPool();

    private final WorkerRunnable<Param,Result> mWorker;
    private final FutureTask<Result> mFuture;

    private final AtomicBoolean mCancelled = new AtomicBoolean(false);
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean(false);

    public AsyncTaskLoader(){
        mWorker = new WorkerRunnable<Param, Result>() {
            @Override
            public Result call() {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    result = doInBackground(mParams);
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                }finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    throw new RuntimeException(
                            "An error occurred while executing doInBackground()", e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                } catch (Throwable t) {
                    throw new RuntimeException(
                            "An error occurred while executing doInBackground()", t);
                }
            }
        };
    }
    protected abstract Result doInBackground(Param... mP);

    public final boolean isCancelled() {
        return mCancelled.get();
    }

    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }

    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return mFuture.get(timeout, unit);
    }

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
    protected void postResult(Result result){

    }
    protected void onPreExecute(){

    }
    public final AsyncTaskLoader<Param,Result> execute(Param... mP) {
        return executeOnExecutor(sDefaultExecutor,mP);
    }

    public final AsyncTaskLoader<Param,Result> executeOnExecutor(Executor exec,Param... mP) {
//        if (mStatus != Status.PENDING) {
//            switch (mStatus) {
//                case RUNNING:
//                    throw new IllegalStateException("Cannot execute task:"
//                            + " the task is already running.");
//                case FINISHED:
//                    throw new IllegalStateException("Cannot execute task:"
//                            + " the task has already been executed "
//                            + "(a task can be executed only once)");
//                default:
//                    throw new IllegalStateException("We should never reach this state");
//            }
//        }
//
//        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = mP;
        exec.execute(mFuture);
        return this;
    }

    private static abstract class WorkerRunnable<P, Result> implements Callable<Result> {
        P[] mParams;
    }
}
